package com.example.codegenerator.domain.impl;

import com.example.codegenerator.common.constant.sql.DatabaseConstant;
import com.example.codegenerator.common.model.bo.TableInfoBO;
import com.example.codegenerator.common.model.bo.TableInfoResultBO;
import com.example.codegenerator.common.model.bo.TableResultBO;
import com.example.codegenerator.common.model.dto.TableLoadDTO;
import com.example.codegenerator.common.model.vo.PageResultVO;
import com.example.codegenerator.common.model.vo.TableResultVO;
import com.example.codegenerator.common.utils.GeneratorUtils;
import com.example.codegenerator.domain.DatabaseDomainApplication;
import com.example.codegenerator.service.DatabaseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import java.util.*;
import java.util.stream.Collectors;

import static com.example.codegenerator.common.constant.sql.SqlConstant.COUNT_SQL_KEY;
import static com.example.codegenerator.common.constant.sql.SqlConstant.DATA_SQL_KEY;

/**
 * 数据提取 领域层 具体实现
 *
 * @author lvshengyu
 * @date 2023/8/17
 */
@Slf4j
@Component
public class DatabaseDomainApplicationImpl implements DatabaseDomainApplication {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 加载当前数据库所有表
     *
     * @param tableLoadDTO 数据表查询对象
     * @return
     */
    @Override
    public PageResultVO<List<TableResultVO>> loadTable(TableLoadDTO tableLoadDTO) {
        DatabaseService databaseService = initDatabaseService();
        tableLoadDTO = Optional.ofNullable(tableLoadDTO).orElse(TableLoadDTO.builder().build());
        // 处理并获取sql
        Map<String, String> sqlMap = databaseService.getSelectSql(tableLoadDTO);

        String countSql = sqlMap.get(COUNT_SQL_KEY);
        log.info("查询数量 sql :{}", "\n" + countSql + ";");
        Integer count = jdbcTemplate.queryForObject(countSql, Integer.class);

        List<TableResultBO> tableResultBOList = null;
        if (GeneratorUtils.isNotNull(count) && count > 0) {
            String dataSql = sqlMap.get(DATA_SQL_KEY);
            log.info("查询数据 sql :{}", "\n" + dataSql + ";");
            tableResultBOList = jdbcTemplate.query(dataSql, new BeanPropertyRowMapper<>(TableResultBO.class));
        }

        List<TableResultVO> tableResultVOList = GeneratorUtils.newArrayList();
        // 当读取db数据不为空再进行数据模型转换
        if (GeneratorUtils.isNotEmpty(tableResultBOList)) {
            tableResultVOList = convertBoToVo(tableResultBOList);
        }

        // 封装返回数据模型
        return PageResultVO.<List<TableResultVO>>builder().pageData(tableResultVOList)
                .pageNum(tableLoadDTO.getPageSize())
                .pageSize(tableLoadDTO.getPageSize())
                .totalCount(count)
                .build();
    }

    /**
     * 数据模型转换 TableResultBO -> TableResultVO
     *
     * @param tableResultBOList 数据表业务对象
     * @return
     */
    private List<TableResultVO> convertBoToVo(List<TableResultBO> tableResultBOList) {
        String tableNames = getTableNames(tableResultBOList);
        log.info("数据表信息 :{}", tableNames);
        List<TableInfoResultBO> tableInfoResultBOList = loadTableColumnsInfo(tableNames);
        // 初始化数据表视图对象集合
        List<TableResultVO> tableResultVOList = GeneratorUtils.newArrayList();

        tableResultBOList.forEach(tableResultBO -> {
            List<TableInfoResultBO> tableInfoBOList = GeneratorUtils.newArrayList();

            tableInfoResultBOList.forEach(tableInfoResultBO -> {
                // 将字段放入对应的数据表业务对象中
                if (tableResultBO.getTableName().equalsIgnoreCase(tableInfoResultBO.getTableName())) {
                    tableInfoBOList.add(tableInfoResultBO);
                }
            });

            TableResultVO tableResultVO = convert(tableResultBO, tableInfoBOList);
            tableResultVOList.add(tableResultVO);
        });
        return tableResultVOList;
    }

    /**
     * 获取读取到的数据库名称 （格式 'a','b','……'）
     *
     * @param tableResultBOList 数据表业务对象
     * @return
     */
    private String getTableNames(List<TableResultBO> tableResultBOList) {
        return tableResultBOList.stream().map(item -> "'" + item.getTableName() + "'").collect(Collectors.joining(","));
    }

    /**
     * 数据模型转换并封装字段集合信息
     *
     * @param tableResultBO   数据表业务对象
     * @param tableInfoBOList 数据表信息业务对象集合
     * @return
     */
    private TableResultVO convert(TableResultBO tableResultBO, List<TableInfoResultBO> tableInfoBOList) {
        tableInfoBOList = sortList(tableInfoBOList);
        TableResultVO tableResultVO = new TableResultVO();
        tableResultVO.setTableName(tableResultBO.getTableName());
        tableResultVO.setTableComment(tableResultBO.getTableComment());
        tableResultVO.setCreateTime(tableResultBO.getCreateTime());
        tableResultVO.setUpdateTime(tableResultBO.getUpdateTime());
        tableResultVO.setTableDetail(formatTableColumns(tableInfoBOList));
        return tableResultVO;
    }

    /**
     * 左括号
     */
    private static final String LEFT_PARENTHESIS = "[";

    /**
     * 右括号
     */
    private static final String RIGHT_PARENTHESIS = "]";

    /**
     * 空字符
     */
    private static final String NULL_STRING = "null";

    /**
     * 逗号
     */
    private static final String COMMA = ",";

    /**
     * 将字段信息转换成固定格式
     *
     * @param tableInfoBOList 数据表信息业务对象集合
     * @return
     */
    private String formatTableColumns(List<TableInfoResultBO> tableInfoBOList) {
        return tableInfoBOList.toString()
                .replace(LEFT_PARENTHESIS, "")
                .replace(RIGHT_PARENTHESIS, "")
                .replace(NULL_STRING, " ")
                .replace(COMMA, "");
    }

    /**
     * 字段信息集合排序
     *
     * @param tableInfoBOList 字段信息集合
     * @return
     */
    private List<TableInfoResultBO> sortList(List<TableInfoResultBO> tableInfoBOList) {
        return tableInfoBOList.stream().sorted(Comparator.comparing(TableInfoResultBO::getOrdinalPosition)).collect(Collectors.toList());
    }

    /**
     * 获取当前数据库下所有数据表字段信息
     *
     * @param tableNames 读取到的数据库名称 （格式 'a','b','……'）
     * @return
     */
    private List<TableInfoResultBO> loadTableColumnsInfo(String tableNames) {
        DatabaseService databaseService = initDatabaseService();
        String sql = databaseService.getTableInfoColumnsSql(tableNames);
        log.info("\n" + sql);
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(TableInfoResultBO.class));
    }

    @Autowired
    private Map<String, DatabaseService> databaseServiceMap;

    /**
     * 初始化数据提取service
     *
     * @return
     */
    private DatabaseService initDatabaseService() {
        String databaseType = getDatabaseConnectInfo();
        return databaseServiceMap.get(databaseType);
    }

    /**
     * 数据库连接路径
     */
    @Value("${spring.datasource.url}")
    private String databaseUrl;

    /**
     * 数据库类型集合
     */
    private static Set<String> databaseTypeSet;

    /**
     * 静态代码块实例化数据库类型集合 (支持数据库 MySql Oracle PostgreSql)
     */
    static {
        databaseTypeSet = new HashSet<>(Arrays.asList(
                DatabaseConstant.MYSQL,
                DatabaseConstant.ORACEL,
                DatabaseConstant.POSTGRESQL));
    }

    /**
     * 获取数据库连接信息
     *
     * @return
     */
    private String getDatabaseConnectInfo() {
        String databaseType = null;

        for (String type : databaseTypeSet) {
            if (databaseUrl.contains(type)) {
                databaseType = type;
                break;
            }
        }

        Assert.notNull(databaseType, "数据库类型为空");
        log.info("数据库类型 :{}", databaseType);
        return databaseType;
    }

    /**
     * 获取数据表信息
     *
     * @param tableName 数据表名称
     * @return
     */
    @Override
    public List<TableInfoBO> loadTableInfo(String tableName) {
        DatabaseService databaseService = initDatabaseService();
        String sql = databaseService.getTableInfoSql(tableName);
        log.info("\n" + sql);
        return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(TableInfoBO.class));
    }

    /**
     * 获取数据表描述信息
     *
     * @param tableName 数据表名称
     * @return
     */
    @Override
    public String getTableComment(String tableName) {
        DatabaseService databaseService = initDatabaseService();
        String sql = databaseService.getTableCommentSql(tableName);
        log.info("\n" + sql);
        return jdbcTemplate.queryForObject(sql, String.class);
    }
}
